// Game.cpp : Implementation of CGame
// NOTE FOR HPC BUILDS
//		/D "STRICT" /D "HPC_BUILD" 
//		ALSO PRECOMPILED HEADERS in Setting/C++ should be "NO" FOR HPC Or Use: for PPC
#include "stdafx.h"
#include "ASpriteCE.h"
#include "Game.h"
#include "MyScriptSite.h"
#include "Commctrl.h"

#define HHTASKBARHEIGHT 26

// scripting vars
IActiveScript *m_pAS;
IActiveScriptParse *m_pASP;
CMyScriptSite *m_pMySite;


/////////////////////////////////////////////////////////////////////////////
// CGame


STDMETHODIMP CGame::TimerStartTimer(long lMilliseconds)
{
	// TODO: Add your implementation code here
	SetTimer(ASPRITE_TIMER, lMilliseconds);

	return S_OK;
}

STDMETHODIMP CGame::TimerStopTimer()
{
	// TODO: Add your implementation code here
	KillTimer(ASPRITE_TIMER);

	return S_OK;
}

LRESULT CGame::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	// fire a mouse event, possibly a collision if collision events are on
	m_MouseX=LOWORD(lParam); m_MouseY=HIWORD(lParam);

	BOOL bFireEvent = TRUE;

	if (m_bFireMouseCollisions) {
		// check for mouse collision with sprite
		// test for collision with any other sprite or tiles
		for (int i=0; i <= m_lNumSprites; i++) {
		  if (m_sprDynamic[i]!=NULL) {
			if (m_sprDynamic[i]->IsAlive()) { 
				RECT rcPoint;
				rcPoint.left=LOWORD(lParam); rcPoint.top=HIWORD(lParam);
				rcPoint.right=LOWORD(lParam)+1; rcPoint.bottom=HIWORD(lParam)+1;

				if (m_sprDynamic[i]->IntersectedRect(&rcPoint)) {
					Fire_SpriteCollisionMouse(i,LOWORD(lParam),HIWORD(lParam), FALSE);
					bFireEvent = FALSE;

					// *ACK* reset MouseX, MouseY
					m_MouseX=-1; m_MouseY=-1;
				
				}
			}
		  }
		}
	} 


	if (bFireEvent) Fire_InputMouseDown(LOWORD(lParam), HIWORD(lParam));

  return 0;
}

LRESULT CGame::OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	m_MouseX=-1; m_MouseY=-1;
	Fire_InputMouseUp(LOWORD(lParam), HIWORD(lParam));

  return 0;
}

LRESULT CGame::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{

	m_MouseX=LOWORD(lParam); m_MouseY=HIWORD(lParam);
	// fire a mouse event, possibly a collision if collision events are on
	BOOL bFireEvent = TRUE;
	if (m_bFireMouseCollisions) {
		// check for mouse collision with sprite
		// test for collision with any other sprite or tiles
		for (int i=0; i <= m_lNumSprites; i++) {
		  if (m_sprDynamic[i]!=NULL) {
			if (m_sprDynamic[i]->IsAlive()) { 
				RECT rcPoint;
				rcPoint.left=LOWORD(lParam); rcPoint.top=HIWORD(lParam);
				rcPoint.right=LOWORD(lParam)+1; rcPoint.bottom=HIWORD(lParam)+1;

				if (m_sprDynamic[i]->IntersectedRect(&rcPoint)) {
					Fire_SpriteCollisionMouse(i,LOWORD(lParam),HIWORD(lParam), TRUE);
					bFireEvent = FALSE;

					// *ACK* reset MouseX, MouseY
					m_MouseX=-1; m_MouseY=-1;
				}
			}
		  }
		}
	} 
	
	
	if (bFireEvent)	Fire_InputMouseMove(LOWORD(lParam), HIWORD(lParam));
	


  return 0;
}

LRESULT CGame::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
  long lKeyCode;
  //BOOL bFireEvent=FALSE;

  GXKeyList gxKeys = GXGetDefaultKeys(GX_NORMALKEYS);

  lKeyCode = (short)wParam;
  if (lKeyCode == gxKeys.vkUp) {
	m_bInputKeyUp = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkDown) {
	m_bInputKeyDown = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkLeft) {
	m_bInputKeyLeft = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkRight) {
	m_bInputKeyRight = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkA) {
	m_bInputKeyA = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkB) {
	m_bInputKeyB = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkC) {
	m_bInputKeyC = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkStart) {
	m_bInputKeyStart = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == 192) {
	m_bInputKeyAux1 = TRUE;
	//bFireEvent = TRUE;
  } else 
  if (lKeyCode == 193) {
	m_bInputKeyAux2 = TRUE;
	//bFireEvent = TRUE;
  } 
  
  //if (bFireEvent) 
  Fire_InputKeyPressed(lKeyCode);

  return 0;
}

LRESULT CGame::OnKeyUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
  long lKeyCode;

  GXKeyList gxKeys = GXGetDefaultKeys(GX_NORMALKEYS);
  BOOL bFireEvent=FALSE;

  lKeyCode = (short)wParam;
  if (lKeyCode == gxKeys.vkUp) {
	m_bInputKeyUp = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkDown) {
	m_bInputKeyDown = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkLeft) {
	m_bInputKeyLeft = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkRight) {
	m_bInputKeyRight = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkA) {
	m_bInputKeyA = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkB) {
	m_bInputKeyB = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkC) {
	m_bInputKeyC = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == gxKeys.vkStart) {
	m_bInputKeyStart = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == 192) {
	m_bInputKeyAux1 = FALSE;
	bFireEvent = TRUE;
  } else 
  if (lKeyCode == 193) {
	m_bInputKeyAux2 = FALSE;
	bFireEvent = TRUE;
  } 
  
  if (bFireEvent) Fire_InputKeyReleased(lKeyCode);

  

  return 0;
}

STDMETHODIMP CGame::DrawInit(DWORD hwnd, int iCustomWidth, int iCustomHeight)
{
	// set up pointer to this object
	DoInit();

	if (hwnd>0)
		m_hwnd = (HWND) hwnd;


	// this is also computed in the OnDraw event
	// but we need to do it here in case window hasn't shown yet
	GetClientRect(&m_rcBounds);
	m_iBoundsHeight = m_rcBounds.bottom - m_rcBounds.top;
	m_iBoundsWidth = m_rcBounds.right - m_rcBounds.left;

    // Create Back Buffer for flipping 
	m_sprDoubleBuffer = new CImageHigh(GetDC());
    if (!m_sprDoubleBuffer->Create(m_iBoundsWidth, m_iBoundsHeight))
		return AtlReportError(GetObjectCLSID(), "DrawInit: Error creating Double Buffer.", IID_IGame, 0);

	
    // Create Background image buffer for erasing sprites
	if (iCustomWidth > m_iBoundsWidth) 
		m_iScrollWidth = iCustomWidth;
	else
		m_iScrollWidth = m_iBoundsWidth;

	if (iCustomHeight > m_iBoundsHeight) 
		m_iScrollHeight = iCustomHeight;
	else
		m_iScrollHeight = m_iBoundsHeight;

	m_sprBackground = new CImageHigh(GetDC());
    if (!m_sprBackground->Create(m_iScrollWidth, m_iScrollHeight))
		return AtlReportError(GetObjectCLSID(), "DrawInit: Error creating Background Buffer.", IID_IGame, 0);

    //set main DC = double buffer DC
    m_hdc = m_sprDoubleBuffer->GetSourceDC();

    // Create Background buffer
	m_lTimeLastFlipBackBuffer = GetTickCount();

	int iRetVal = GXOpenInput();

	// seed random number generator
	DWORD dw = GetTickCount();
	srand(dw);

	// fill to black initially
	DrawFloodFill();
	DrawFlipBackBuffer();

	
	// default text attributes
	TextSetAttributes(_T("Tahoma"), 12, 10, FALSE, FALSE, FALSE, 0);

	m_bInitialized = TRUE;

	TimerPauseTimer(FALSE);

	return S_OK;
}	

STDMETHODIMP CGame::DrawDisable()
{
	TimerStopTimer();

	// let go of keys
	GXCloseInput();

	// delete sprites
	for (int i=0; i<=m_lNumSprites; i++) {
		SpriteDelete(i);
	}

	if (m_sprDoubleBuffer!=NULL) {
		delete m_sprDoubleBuffer;
		m_sprDoubleBuffer = NULL;
	}
	if (m_sprBackground!=NULL) {
		delete m_sprBackground;
		m_sprBackground = NULL;
	}

	m_lNumSprites = 0;

	// delete sounds
	for (i=0; i<=m_lNumSounds; i++)
		SoundDelete(i);

	// delete timers
	for (i=0; i<=m_iNumTimers; i++) {
		if (m_tmrCustom[i] != NULL) {
			delete m_tmrCustom[i];
			m_tmrCustom[i] = NULL;
		}
	}

	return 0;
}

STDMETHODIMP CGame::DrawFloodFill()
{
	HRGN hRgn;          // Handle to a region object  
	HBRUSH hBrush;      // Handle to a brush object 
	RECT rect = { 1, 1, m_iBoundsWidth, m_iBoundsHeight };


	// Create a rectangular region.
	hRgn = CreateRectRgn (0, 0, rect.right, rect.bottom);

	// Create a solid brush.
	hBrush = CreateSolidBrush (RGB(m_DrawBackRed,m_DrawBackGreen,m_DrawBackBlue)); 

	// Fill the region out with the created brush.
	FillRgn (m_hdc, hRgn, hBrush);

	// Delete the rectangular region. 
	DeleteObject (hRgn);

	// Delete the brush object and free all resources associated with it.
	DeleteObject (hBrush);
	
	return 0;
}

STDMETHODIMP CGame::DrawSetPixel(int x, int y, BOOL bDrawBackground)
{

	COLORREF  clrSet = RGB(m_DrawRed,m_DrawGreen,m_DrawBlue);

	// Draw to back buffer
	SetPixel (m_hdc, x, y, clrSet);

	// Draw to background image as well
	if (bDrawBackground) SetPixel (m_sprBackground->GetSourceDC(), x, y, clrSet);

	return 0;
}


STDMETHODIMP CGame::DrawSetDrawColor(int red, int green, int blue)
{
	m_DrawRed= red;
	m_DrawGreen = green;
	m_DrawBlue = blue;

	return S_OK;
}




LRESULT CGame::OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{

	return 0;
}

LRESULT CGame::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	//HDC hdc;
	//PAINTSTRUCT ps;

	//hdc = BeginPaint(&ps);
	//if (GetForegroundWindow() == m_hwnd)
	//	DrawFloodFill();
	//EndPaint(&ps);

	return 0;
}



STDMETHODIMP CGame::DrawSetBackColor(int red, int green, int blue)
{
	// TODO: Add your implementation code here
	m_DrawBackRed= red;
	m_DrawBackGreen = green;
	m_DrawBackBlue = blue;

	return S_OK;
}

STDMETHODIMP CGame::SpriteDefine(long lSpriteNum, BSTR sFileName, long iXPosBMP,
								 long iYPosBMP, long iFrameWidth, long iFrameHeight, 
								 long iFrameCount, long iFramesPerRow)
{
	// TODO: Add your implementation code here
	HDC hdc;
	int iRetVal;

	if (!m_bInitialized)
		return AtlReportError(GetObjectCLSID(), "SpriteDefine: You must first call DrawInit before SpriteDefine.", IID_IGame, 0);

	
	hdc = m_hdc;

	if (m_sprDynamic[lSpriteNum]!=NULL) {
		delete(m_sprDynamic[lSpriteNum]);
		m_sprDynamic[lSpriteNum] = NULL;
	};

	// optional AppPath property is prefixed
	_bstr_t bsFileName;
	if (m_sAppPath!=NULL) {
		bsFileName += m_sAppPath;
		bsFileName += sFileName;
	} else
		bsFileName = sFileName;

	m_sprDynamic[lSpriteNum] = new CSpriteHigh(hdc);
	iRetVal=m_sprDynamic[lSpriteNum]->LoadAnimSeq(iXPosBMP, iYPosBMP,iFrameCount,bsFileName, iFrameWidth, iFrameHeight, iFramesPerRow);
	
	if (iRetVal!=0) {
		LPCSTR lpcsError=(const char *) malloc(200); //calloc(200, sizeof(char));

		switch (iRetVal) {
			case -1:
				return AtlReportError(GetObjectCLSID(), "SpriteDefine: parameter iFrameCount is too big.", IID_IGame, 0);
				break;
			case -2:
				return AtlReportError(GetObjectCLSID(), "SpriteDefine: FrameWidth and/or FrameHeight too big.", IID_IGame, 0);
				break;
			case -3:
				sprintf( (char *)lpcsError, "SpriteDefine: Could not load sprite from bsFileName %s", bsFileName);
				return AtlReportError(GetObjectCLSID(), lpcsError, IID_IGame, 0);
				break;
			case -4:
				sprintf( (char *)lpcsError, "SpriteDefine: Could not create sprite from bsFileName %s", bsFileName);
				return AtlReportError(GetObjectCLSID(), lpcsError, IID_IGame, 0);
				break;
			default:
				return AtlReportError(GetObjectCLSID(), "SpriteDefine: Unknown error.", IID_IGame, 0);
		}
	}
	m_sprDynamic[lSpriteNum]->SetAlive(TRUE);
	m_sprDynamic[lSpriteNum]->SetVelocity(0);
	m_sprDynamic[lSpriteNum]->SetXDir(0);
	m_sprDynamic[lSpriteNum]->SetYDir(0);
	m_sprDynamic[lSpriteNum]->SetXSpeed(0);
	m_sprDynamic[lSpriteNum]->SetYSpeed(0);
	m_sprDynamic[lSpriteNum]->SetDirection(0);
	m_sprDynamic[lSpriteNum]->SetVelocity(0);


	if (lSpriteNum > m_lNumSprites)
		m_lNumSprites = lSpriteNum;

	// maintain Z Order
	DoZorder();

	return S_OK;
}

STDMETHODIMP CGame::SpriteCopy(long lSrcSpriteNum, long lDestSpriteNum)
{
	// make a copy of a sprite
	if (m_sprDynamic[lSrcSpriteNum] == NULL) {
		return AtlReportError(GetObjectCLSID(), "SpriteCopy: You have not defined the source sprite yet. Check lSrcSpriteNum parameter.", IID_IGame, 0);
	}

	if (m_sprDynamic[lDestSpriteNum] != NULL)  {
		SpriteDelete(lDestSpriteNum);
	}

	m_sprDynamic[lDestSpriteNum] = new CSpriteHigh(m_hdc);
	m_sprDynamic[lDestSpriteNum]->SetIsACopy(TRUE);

	// copy frame pointers over
	int i;
	for (i=0; i<m_sprDynamic[lSrcSpriteNum]->GetTotalFrames(); i++) {
		m_sprDynamic[lDestSpriteNum]->SetFrameSource(i, m_sprDynamic[lSrcSpriteNum]->GetFrameSource(i));
	}

	m_sprDynamic[lDestSpriteNum]->SetTotalFrames(m_sprDynamic[lSrcSpriteNum]->GetTotalFrames());
	m_sprDynamic[lDestSpriteNum]->SetFrame(m_sprDynamic[lSrcSpriteNum]->GetFrame());
	m_sprDynamic[lDestSpriteNum]->SetImageSize(m_sprDynamic[lSrcSpriteNum]->ImageWidth(), m_sprDynamic[lSrcSpriteNum]->ImageHeight());
	
	m_sprDynamic[lDestSpriteNum]->SetAlive(m_sprDynamic[lSrcSpriteNum]->IsAlive());
	m_sprDynamic[lDestSpriteNum]->SetVelocity(m_sprDynamic[lSrcSpriteNum]->GetVelocity());
	m_sprDynamic[lDestSpriteNum]->SetXDir(m_sprDynamic[lSrcSpriteNum]->GetXDir());
	m_sprDynamic[lDestSpriteNum]->SetYDir(m_sprDynamic[lSrcSpriteNum]->GetYDir());
	m_sprDynamic[lDestSpriteNum]->SetXSpeed(m_sprDynamic[lSrcSpriteNum]->GetXSpeed());
	m_sprDynamic[lDestSpriteNum]->SetYSpeed(m_sprDynamic[lSrcSpriteNum]->GetYSpeed());
	m_sprDynamic[lDestSpriteNum]->SetDirection(m_sprDynamic[lSrcSpriteNum]->GetDirection());
	m_sprDynamic[lDestSpriteNum]->SetVelocity(m_sprDynamic[lSrcSpriteNum]->GetVelocity());


	m_sprDynamic[lDestSpriteNum]->SetAutoFrameRate(m_sprDynamic[lSrcSpriteNum]->GetAutoFrameRate());
	m_sprDynamic[lDestSpriteNum]->SetSaveBackGround(m_sprDynamic[lSrcSpriteNum]->GetSaveBackGround());
	m_sprDynamic[lDestSpriteNum]->SetFirstFrame(m_sprDynamic[lSrcSpriteNum]->GetFirstFrame());
	m_sprDynamic[lDestSpriteNum]->SetLoopFrames(m_sprDynamic[lSrcSpriteNum]->GetLoopFrames());
	m_sprDynamic[lDestSpriteNum]->SetLastFrame(m_sprDynamic[lSrcSpriteNum]->GetLastFrame());


	if (lDestSpriteNum > m_lNumSprites)
		m_lNumSprites = lDestSpriteNum;

	// maintain Z Order
	DoZorder();
	
	// now set "real" copied ZOrder
	m_sprDynamic[lDestSpriteNum]->SetZOrder(m_sprDynamic[lSrcSpriteNum]->GetZOrder());
	DoZorder();

	return S_OK;
}

STDMETHODIMP CGame::DrawFlipBackBuffer()
{

	// test collisions
	long i, j, k, m;
	int iNumMoves;
	POINT ptColl;
	float iXGoal, iYGoal, iRatio;

	// test for collisions
	int iX, iY, iWidth, iHeight;
	for (i=0; i <= m_lNumSprites; i++) {
	  if (m_sprDynamic[i] != NULL) {
	    BOOL bOnScreen=FALSE;
	    get_SpriteOnScreen(i, &bOnScreen);
	    if (bOnScreen && m_sprDynamic[i]->IsAlive()) {

			// test for collision with edges of screen
			iX = m_sprDynamic[i]->GetX(); iY = m_sprDynamic[i]->GetY();
			iWidth = m_sprDynamic[i]->ImageWidth() - m_sprDynamic[i]->GetShrinkX(); iHeight = m_sprDynamic[i]->ImageHeight() - m_sprDynamic[i]->GetShrinkY();
			if (iX+iWidth >= m_rcBounds.right && iY+iHeight >= m_rcBounds.bottom)
				// collision with southeast
				Fire_SpriteCollisionEdge(i,2);
			else
			if (iX <= -m_sprDynamic[i]->GetShrinkX() && iY+iHeight >= m_rcBounds.bottom)
				// collision with southwest
				Fire_SpriteCollisionEdge(i,4);
			else
			if (iX <= m_sprDynamic[i]->GetShrinkX() && iY < -m_sprDynamic[i]->GetShrinkY())
				// collision with northwest
				Fire_SpriteCollisionEdge(i,6);
			else
			if (iX+iWidth >= m_rcBounds.right && iY < -m_sprDynamic[i]->GetShrinkY())
				// collision with northeast
				Fire_SpriteCollisionEdge(i,8);
			else
			if (iX+iWidth >= m_rcBounds.right)
				// collision with east
				Fire_SpriteCollisionEdge(i,1);
			else
			if (iY+iHeight >= m_rcBounds.bottom)
				// collision with south
				Fire_SpriteCollisionEdge(i,3);
			else
			if (iX <= -m_sprDynamic[i]->GetShrinkX())
				// collision with west
				Fire_SpriteCollisionEdge(i,5);
			else
			if (iY <= -m_sprDynamic[i]->GetShrinkY())
				// collision with north
				Fire_SpriteCollisionEdge(i,7);




			// test for collision with mouse
			if (m_bFireMouseCollisions) {
				RECT rcPoint;
				rcPoint.left=m_MouseX; rcPoint.top=m_MouseY;
				rcPoint.right=m_MouseX+1; rcPoint.bottom=m_MouseY+1;

				if (m_sprDynamic[i]->IntersectedRect(&rcPoint)) {
					Fire_SpriteCollisionMouse(i,m_MouseX,m_MouseY, TRUE);
					
					// *ACK* reset MouseX, MouseY
					m_MouseX=-1; m_MouseY=-1;

				}
			} 

			
			// test for collision with any other sprite or tiles
			for (j=0; j <= m_lNumSprites; j++) {
				if (m_sprDynamic[j] != NULL) {
					// don't test sprite against itself
					if (i!=j && m_sprDynamic[j]->IsAlive() && m_sprDynamic[i]->IsAlive()) { 
						if (m_sprDynamic[i]->Intersected(m_sprDynamic[j], ptColl)) {

							if (m_sprDynamic[i]->CheckCollisionBySprite()) {
							  if (m_sprDynamic[i]->ShouldFireCollision(j)) {
							    Fire_SpriteCollision(i,j,ptColl.x, ptColl.y);
							  }
							} else {
							Fire_SpriteCollision(i,j,ptColl.x, ptColl.y);
						  }
						
						}
					}
				}
			}
		  }
	  }
	}
	

	// move sprites and draw
	for (j=0; j<=m_lNumSprites; j++) {
		// get next sprite in display order
		i=m_lDisplayOrder[j];

		if (m_sprDynamic[i] != NULL) {
			if (m_sprDynamic[i]->IsAlive()) {
				BOOL bOnScreen=FALSE;
				get_SpriteOnScreen(i, &bOnScreen);
				// tile collisions
				BOOL bCanMove = TRUE;
				if (m_bCheckTileCollisions) get_TileSpriteCanMove(i, m_sprDynamic[i]->GetDirection(), m_sprDynamic[i]->GetVelocity(), &bCanMove);
				if (bCanMove) {
					// check if this is OK frame to update position
					m_sprDynamic[i]->m_iStoreVelocityCurrentIncrement--;
					if (m_sprDynamic[i]->m_iStoreVelocityCurrentIncrement <= 0) {
						if (m_sprDynamic[i]->GetSpriteCollisionAccuracy()>0) {
							// use "more accurate" collision detection
							iRatio = ((float)m_sprDynamic[i]->GetSpriteCollisionAccuracy()/(float)m_sprDynamic[i]->GetVelocity());
							iXGoal = (float) m_sprDynamic[i]->GetX() + m_sprDynamic[i]->GetXSpeed();
							iYGoal = (float) m_sprDynamic[i]->GetY() + m_sprDynamic[i]->GetYSpeed();
							iNumMoves = 1/iRatio;
							for (k=0;k<iNumMoves;k++) {
								m_sprDynamic[i]->IncX(m_sprDynamic[i]->GetXSpeed()*iRatio);
								m_sprDynamic[i]->IncY(m_sprDynamic[i]->GetYSpeed()*iRatio);
								// test for collision with any other sprite
								for (m=0; m <= m_lNumSprites; m++) {
									if (m_sprDynamic[m] != NULL) {
										// don't test sprite against itself
										if (i!=m && m_sprDynamic[m]->IsAlive() && m_sprDynamic[i]->IsAlive()) { 
											if (m_sprDynamic[i]->Intersected(m_sprDynamic[m], ptColl)) {
												Fire_SpriteCollision(i,m,ptColl.x, ptColl.y);
											}
										}
									}
								}
							}
							m_sprDynamic[i]->SetX(iXGoal);
							m_sprDynamic[i]->SetY(iYGoal);
						} else {
							// use "normal" move - no intermediate collision detection
							m_sprDynamic[i]->IncX(m_sprDynamic[i]->GetXSpeed());
							m_sprDynamic[i]->IncY(m_sprDynamic[i]->GetYSpeed());
						}
						m_sprDynamic[i]->m_iStoreVelocityCurrentIncrement = m_sprDynamic[i]->GetVelocityIncrement();
					}
				} else {
					if (bOnScreen) 
						if (m_bTileExtendedCollisions) {
						  for (int m=0;m<m_bTileNumCollisions;m++)
							  Fire_SpriteCollisionTileEx(i, m_sprDynamic[i]->GetDirection(), m_sprDynamic[i]->GetVelocity(), m_bTileCollisionsX[m], m_bTileCollisionsY[m], m_bTileCollisionsTileNum[m]);
						} else {
						  Fire_SpriteCollisionTile(i, m_sprDynamic[i]->GetDirection(), m_sprDynamic[i]->GetVelocity());
						}
				}

				// display sprites
				if (m_sprDynamic[i]->GetAutoDraw()) {
					if (bOnScreen) 
						SpriteDisplay(i);
				}
				if (m_sprDynamic[i]->GetAutoFrameRate() > 0) {
					m_sprDynamic[i]->IncAutoFrameCount();
					if (m_sprDynamic[i]->GetAutoFrameCount()==0)
						m_sprDynamic[i]->NextFrame();
				}
			} 
		}		
	}

	//draw double buffer to screen
    HDC hdc = GetDC();

    BitBlt(hdc, 0, 0, m_iBoundsWidth, m_iBoundsHeight, 
        m_hdc, 0, 0, SRCCOPY);

    //release DC
    ReleaseDC(hdc);

	// erase sprites from backbuffer
	for (i=0; i<=m_lNumSprites; i++)
	{
		if (m_sprDynamic[i] != NULL) {
			if (m_sprDynamic[i]->GetShouldErase()) {
				//m_sprDynamic[i]->RestoreUnder(m_hdc);
				DrawRedrawBackground(m_sprDynamic[i]->GetX(),
					m_sprDynamic[i]->GetY(),
					m_sprDynamic[i]->ImageWidth(),
					m_sprDynamic[i]->ImageHeight());
			}
			// make this last erase if sprite is inactive
			if (!m_sprDynamic[i]->IsAlive()) {
				m_sprDynamic[i]->SetShouldErase(FALSE);
			}
		}
	}
	// get frame rate stats
	long lTicks = GetTickCount();
	m_lLastFrameTime = (lTicks - m_lTimeLastFlipBackBuffer);
	m_lTimeLastFlipBackBuffer= lTicks;


	// check for custom timer fires
	for (i=0; i<=m_iNumTimers; i++) {
	  if (m_tmrCustom[i] != NULL) {
		if (m_tmrCustom[i]->GetValue() > 0) {
			m_tmrCustom[i]->SetValue(m_tmrCustom[i]->GetValue()-1);
		} else {
			if (m_tmrCustom[i]->GetValue() == 0) {
				// time to fire timer
				Fire_TimerCustomFired(i);
				if (m_tmrCustom[i]->GetRepeat()) {
					m_tmrCustom[i]->SetValue(m_tmrCustom[i]->GetDelay());
				} else {
					m_tmrCustom[i]->SetValue(-1);
				}

			}
		}
      }
	}

	return S_OK;
}

STDMETHODIMP CGame::SpriteDisplay(long lSpriteNum)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteDisplay: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// store background under sprite
	if (m_sprDynamic[lSpriteNum]->IsStretched()) {
		//m_sprDynamic[lSpriteNum]->SaveUnder(m_sprBackground->GetSourceDC(), m_iScrollOffsetX, m_iScrollOffsetY);
		m_sprDynamic[lSpriteNum]->StretchBlit(RGB(m_DrawTransRed,m_DrawTransGreen,m_DrawTransBlue));
	} else {
		//m_sprDynamic[lSpriteNum]->SaveUnder(m_sprBackground->GetSourceDC(), m_iScrollOffsetX, m_iScrollOffsetY);
		m_sprDynamic[lSpriteNum]->TransBlit(RGB(m_DrawTransRed,m_DrawTransGreen,m_DrawTransBlue));
	}

	return S_OK;
}

STDMETHODIMP CGame::DrawSetTransparentColor(int red, int green, int blue)
{
	// TODO: Add your implementation code here
	m_DrawTransRed = red;
	m_DrawTransGreen = green;
	m_DrawTransBlue = blue;
	return S_OK;
}

STDMETHODIMP CGame::SpriteSetPosition(long lSpriteNum, long lX, long lY)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteSetPosition: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// TODO: Add your implementation code here
	m_sprDynamic[lSpriteNum]->SetX((float) lX);
	m_sprDynamic[lSpriteNum]->SetY((float) lY);
	return S_OK;
}


STDMETHODIMP CGame::DrawBackgroundImage(BSTR sFileName, int lX, int lY)
{

	// optional AppPath property is prefixed
	_bstr_t bsFileName;
	if (m_sAppPath!=NULL) {
		bsFileName += m_sAppPath;
		bsFileName += sFileName;
	} else
		bsFileName = sFileName;

	
	m_sprBackgroundImage = new CImageHigh(m_hdc);

	if (!m_sprBackgroundImage->Load(bsFileName))
		return AtlReportError(GetObjectCLSID(), "DrawBackgroundImage: Error loading image.", IID_IGame, 0);


	else {
		m_sprBackgroundImage->BitBlit(lX,lY);
		// also copy to background buffer used to erase sprites
		m_sprBackgroundImage->BitBlit(m_sprBackground->GetSourceDC(),lX,lY);
	}

	delete m_sprBackgroundImage;
	m_sprBackgroundImage=NULL;
	return S_OK;
}


STDMETHODIMP CGame::DrawBackgroundSprite(long lSpriteNum, int lX, int lY)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "DrawBackgroundSprite: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// Draw a sprite to the double buffer and permanent background
	m_sprDynamic[lSpriteNum]->TransBlit(m_sprDoubleBuffer->GetSourceDC(),lX,lY,RGB(m_DrawTransRed,m_DrawTransGreen,m_DrawTransBlue));
	m_sprDynamic[lSpriteNum]->TransBlit(m_sprBackground->GetSourceDC(),lX,lY,RGB(m_DrawTransRed,m_DrawTransGreen,m_DrawTransBlue));

	return S_OK;
}

STDMETHODIMP CGame::DrawRedrawBackground(int x, int y, int width, int height)
{
	// Redraw a portion of the background image
	// taking into effect scrolled offsets

	BitBlt(m_hdc, x, y, width, height, m_sprBackground->GetSourceDC(), x+m_iScrollOffsetX, y+m_iScrollOffsetY, SRCCOPY);

	return S_OK;
}

STDMETHODIMP CGame::DrawBackgroundScroll(int iAngle, int iDistance)
{
	BOOL bUpdateX=FALSE, bUpdateY=FALSE;
	float lDistanceX, lDistanceY;

	float fRads = iAngle* 0.0174532925199433;
	lDistanceX = ((float) iDistance*cos(fRads));
	lDistanceY = ((float) iDistance*sin(fRads));
	
	if (m_iScrollOffsetX+lDistanceX > 0 && m_iScrollOffsetX+lDistanceX+m_iBoundsWidth< m_iScrollWidth) {
		m_iScrollOffsetX+=lDistanceX;
		bUpdateX = TRUE;
	}
	if (m_iScrollOffsetY+lDistanceY > 0 && m_iScrollOffsetY+lDistanceY+m_iBoundsHeight < m_iScrollHeight) {
		m_iScrollOffsetY+=lDistanceY;
		bUpdateY = TRUE;
	}

	
	BitBlt(m_sprDoubleBuffer->GetSourceDC(), 0, 0, m_iBoundsWidth, m_iBoundsHeight, m_sprBackground->GetSourceDC(), m_iScrollOffsetX, m_iScrollOffsetY, SRCCOPY);

	for (int i=0; i<=m_lNumSprites; i++) {
		if (bUpdateX) 
			if (m_sprDynamic[i]!=NULL)
				m_sprDynamic[i]->SetX(m_sprDynamic[i]->GetX() - lDistanceX);
		if (bUpdateY) 
			if (m_sprDynamic[i]!=NULL)
				m_sprDynamic[i]->SetY(m_sprDynamic[i]->GetY() - lDistanceY);
	}

	return S_OK;
}


STDMETHODIMP CGame::DrawBackgroundScrollSetOffsets(int iOffsetX, int iOffSetY)
{
	// scroll to a particular x,y position in the tile world
	BOOL bUpdateX=FALSE, bUpdateY=FALSE;
	float lDistanceX, lDistanceY;

	lDistanceX = (iOffsetX - m_iScrollOffsetX);
	lDistanceY = (iOffSetY - m_iScrollOffsetY);
	

	if (m_iScrollOffsetX+lDistanceX >= 0 && m_iScrollOffsetX+lDistanceX+m_iBoundsWidth <= m_iScrollWidth) {
		m_iScrollOffsetX+=lDistanceX;
		bUpdateX = TRUE;
	}
	if (m_iScrollOffsetY+lDistanceY >= 0 && m_iScrollOffsetY+lDistanceY+m_iBoundsHeight <= m_iScrollHeight) {
		m_iScrollOffsetY+=lDistanceY;
		bUpdateY = TRUE;
	}

	
	BitBlt(m_sprDoubleBuffer->GetSourceDC(), 0, 0, m_iBoundsWidth, m_iBoundsHeight, m_sprBackground->GetSourceDC(), m_iScrollOffsetX, m_iScrollOffsetY, SRCCOPY);

	for (int i=0; i<=m_lNumSprites; i++) {
		if (bUpdateX) 
			if (m_sprDynamic[i]!=NULL)
				m_sprDynamic[i]->SetX(m_sprDynamic[i]->GetX() - lDistanceX);
		if (bUpdateY) 
			if (m_sprDynamic[i]!=NULL)
				m_sprDynamic[i]->SetY(m_sprDynamic[i]->GetY() - lDistanceY);
	}

	return S_OK;
}

STDMETHODIMP CGame::DrawBackgroundScrollGetOffsets(VARIANT *iOffsetX, VARIANT *iOffsetY)
{
	// Return the offsets of the scrolling position
	CComVariant vOffsetX(*iOffsetX), vOffsetY(*iOffsetY);

	vOffsetX.ChangeType(VT_I2);
	vOffsetX.iVal = m_iScrollOffsetX;
	vOffsetX.Detach(iOffsetX);

	vOffsetY.ChangeType(VT_I2);
	vOffsetY.iVal = m_iScrollOffsetY;
	vOffsetY.Detach(iOffsetY);

	return S_OK;
}

STDMETHODIMP CGame::get_TimerGetTickCount(long *pVal)
{
	// TODO: Add your implementation code here
	long lTicks = GetTickCount();
	*pVal = lTicks;
	return S_OK;
}

STDMETHODIMP CGame::get_TimerGetFrameRate(long *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_lLastFrameTime;

	return S_OK;
}



STDMETHODIMP CGame::SoundLoad(long lSoundNum, BSTR sFileName)
{
	// optional AppPath property is prefixed
	_bstr_t bsFileName;
	if (m_sAppPath!=NULL) {
		bsFileName += m_sAppPath;
		bsFileName += sFileName;
	} else
		bsFileName = sFileName;

	if (sndWaves[lSoundNum] != NULL) {
		delete sndWaves[lSoundNum];
		sndWaves[lSoundNum] = NULL;
	}

	sndWaves[lSoundNum] = new CWaveOut();
	sndWaves[lSoundNum]->Load(bsFileName);

	m_lNumSounds++;

	return S_OK;
}

STDMETHODIMP CGame::SoundPlay(long lSoundNum)
{
	if (sndWaves[lSoundNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SoundPlay: You have not defined that sound yet. Check lSoundNum parameter.", IID_IGame, 0);
	sndWaves[lSoundNum]->Play();
	//m_sndMixer->SoundPlay(lSoundNum);

	return S_OK;
}


STDMETHODIMP CGame::DrawGetPixel(int x, int y, VARIANT *red, VARIANT *green, VARIANT *blue)
{
	COLORREF  clrGet;
	CComVariant lRed(*red), lGreen(*green), lBlue(*blue);

	// Draw to background image as well
	clrGet = GetPixel (m_sprBackground->GetSourceDC(), x, y);
	
	lRed.ChangeType(VT_I2);
	lRed.iVal = GetRValue(clrGet);
	lRed.Detach(red);

	lGreen.ChangeType(VT_I2);
	lGreen.iVal = GetGValue(clrGet);
	lGreen.Detach(green);

	lBlue.ChangeType(VT_I2);
	lBlue.iVal = GetBValue(clrGet);
	lBlue.Detach(blue);
	

	return S_OK;
}

STDMETHODIMP CGame::SpriteSetDirection(long lSpriteNum, int iAngle, int iVelocity)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteSetDirection: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// TODO: Add your implementation code here
	float fRads = iAngle* 0.0174532925199433;
	m_sprDynamic[lSpriteNum]->SetXSpeed((float) iVelocity*cos(fRads));
	m_sprDynamic[lSpriteNum]->SetYSpeed((float) iVelocity*sin(fRads));
	m_sprDynamic[lSpriteNum]->SetDirection(iAngle);
	m_sprDynamic[lSpriteNum]->SetVelocity(iVelocity);
	return S_OK;
}

STDMETHODIMP CGame::get_UtilGetAngleBetween(int x1, int y1, int x2, int y2, short *pVal)
{
	// TODO: Add your implementation code here
	int iAngle;
	int iCalc = 57.2727272727272727 * atan2(x2-x1,y2-y1);
	if (iCalc >= 0 && iCalc <= 90) 
		iAngle = 90 - iCalc;
	else
	if (iCalc > -180 && iCalc < 0)
		iAngle = abs(iCalc) + 90;
	else
		iAngle = 180 - iCalc + 270;

	*pVal = iAngle;
	return S_OK;

}

STDMETHODIMP CGame::get_InputKeyUp(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyUp;
	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyDown(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyDown;
	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyLeft(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyLeft;
	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyRight(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyRight;
	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyA(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyA;

	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyB(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyB;

	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyC(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyC;

	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyStart(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyStart;

	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyAux1(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyAux1;

	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyAux2(BOOL *pVal)
{
	// TODO: Add your implementation code here
	*pVal = m_bInputKeyAux2;

	return S_OK;
}

STDMETHODIMP CGame::get_InputKeyDirection(short *pVal)
{
	short bDirection=-1;

	// determine Key direction from pressed keys
	if (m_bInputKeyRight && m_bInputKeyDown)
		bDirection = 1;
	else
	if (m_bInputKeyDown && m_bInputKeyLeft)
		bDirection = 3;
	else
	if (m_bInputKeyLeft && m_bInputKeyUp) 
		bDirection = 5;
	else
	if (m_bInputKeyUp && m_bInputKeyRight)
		bDirection = 7;
	else
	if (m_bInputKeyRight) bDirection = 0;
	else
	if (m_bInputKeyDown) bDirection = 2;
	else
	if (m_bInputKeyLeft) bDirection = 4;
	else
	if (m_bInputKeyUp) bDirection = 6;

	*pVal = bDirection;
	return S_OK;
}

STDMETHODIMP CGame::SpriteGetDirection(long lSpriteNum, VARIANT *iAngle, VARIANT *iVelocity)
{

	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteGetDirection: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	
	// TODO: Add your implementation code here
	CComVariant vAngle(*iAngle), vVelocity(*iVelocity);

	vAngle.ChangeType(VT_I2);
	vAngle.iVal = m_sprDynamic[lSpriteNum]->GetDirection();
	vAngle.Detach(iAngle);

	vVelocity.ChangeType(VT_I2);
	vVelocity.iVal = m_sprDynamic[lSpriteNum]->GetVelocity();
	vVelocity.Detach(iVelocity);

	return S_OK;
}

LRESULT CGame::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{

  if (wParam!=ASPRITE_TIMER) return 0;
  
  if (!m_bPaused && GetActiveWindow()==m_hwnd) {
#ifndef HPC_BUILD
    if (m_bNeedFullScreenRefresh) {
	  DrawFullScreen(m_bShowTaskBar,m_bShowSIP,m_bShowStartIcon);
	  m_bNeedFullScreenRefresh = FALSE;
	}
#endif
	Fire_TimerFired();
	if (m_bAutoFlipBackBuffer) DrawFlipBackBuffer();
  } else {
    if (GetActiveWindow()!=m_hwnd && m_bAutoFullScreen)
		m_bNeedFullScreenRefresh = TRUE;
  } 	  
  return 0;
}

STDMETHODIMP CGame::get_DrawAutoFlipBackBuffer(BOOL *pVal)
{
	*pVal = m_bAutoFlipBackBuffer;

	return S_OK;
}

STDMETHODIMP CGame::put_DrawAutoFlipBackBuffer(BOOL newVal)
{
	m_bAutoFlipBackBuffer = newVal;

	return S_OK;
}

STDMETHODIMP CGame::get_SpriteAutoDisplay(long lSpriteNum, BOOL *pVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteAutoDisplay: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	*pVal = m_sprDynamic[lSpriteNum]->GetAutoDraw();
	return S_OK;
}

STDMETHODIMP CGame::put_SpriteAutoDisplay(long lSpriteNum, BOOL newVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteAutoDisplay: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	m_sprDynamic[lSpriteNum]->SetAutoDraw(newVal);
	return S_OK;
}

STDMETHODIMP CGame::get_SpriteFrameRate(long lSpriteNum, short *pVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteFrameRate: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	*pVal = m_sprDynamic[lSpriteNum]->GetAutoFrameRate();
	return S_OK;
}

STDMETHODIMP CGame::put_SpriteFrameRate(long lSpriteNum, short newVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteFrameRate: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	m_sprDynamic[lSpriteNum]->SetAutoFrameRate(newVal);


	return S_OK;
}

STDMETHODIMP CGame::TimerPauseTimer(BOOL bPause)
{
	m_bPaused = bPause;

	return S_OK;
}


STDMETHODIMP CGame::SpriteSetNextFrame(long lSpriteNum)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteSetNextFrame: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	m_sprDynamic[lSpriteNum]->NextFrame();
	return S_OK;
}

STDMETHODIMP CGame::SpriteSetPrevFrame(long lSpriteNum)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteSetPrevFrame: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	m_sprDynamic[lSpriteNum]->PrevFrame();

	return S_OK;
}

STDMETHODIMP CGame::SpriteSetFrameRange(long lSpriteNum, int iFirstFrame, int iLastFrame, BOOL bLoopFrames)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteSetFrameRange: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// Set the Frame range for Sprites with SpriteFrameRate set
	m_sprDynamic[lSpriteNum]->SetFirstFrame(iFirstFrame);
	m_sprDynamic[lSpriteNum]->SetLastFrame(iLastFrame);
	m_sprDynamic[lSpriteNum]->SetLoopFrames(bLoopFrames);
	short iCurrFrame = m_sprDynamic[lSpriteNum]->GetFrame();
	if ((iCurrFrame < iFirstFrame && iCurrFrame < iLastFrame) ||
		(iCurrFrame > iFirstFrame && iCurrFrame > iLastFrame))
		m_sprDynamic[lSpriteNum]->SetFrame(iFirstFrame);
	//if (!bLoopFrames) m_sprDynamic[lSpriteNum]->SetFrame(iFirstFrame);
	//if (iFirstFrame > iLastFrame) m_sprDynamic[lSpriteNum]->SetFrame(iFirstFrame);

	//m_sprDynamic[lSpriteNum]->SetFrame(iFirstFrame);
	return S_OK;
}

STDMETHODIMP CGame::SpriteGetFrameRange(long lSpriteNum, VARIANT *iFirstFrame, VARIANT *iLastFrame, VARIANT *bLoopFrames)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteGetFrameRange: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// Get the Frame range for Sprites with SpriteFrameRate set
	CComVariant vFirst(*iFirstFrame), vLast(*iLastFrame), vLoopFrames(*bLoopFrames);

	vFirst.ChangeType(VT_I2);
	vFirst.iVal = m_sprDynamic[lSpriteNum]->GetFirstFrame();
	vFirst.Detach(iFirstFrame);

	vLast.ChangeType(VT_I2);
	vLast.iVal = m_sprDynamic[lSpriteNum]->GetLastFrame();
	vLast.Detach(iLastFrame);

	vLoopFrames.ChangeType(VT_BOOL);
	vLoopFrames.boolVal = m_sprDynamic[lSpriteNum]->GetLoopFrames();
	vLoopFrames.Detach(bLoopFrames);

	return S_OK;
}

STDMETHODIMP CGame::TextSetAttributes(BSTR sFaceName, long lPoints, long lWeight, BOOL bItalic, BOOL bUnderline, BOOL bStrikeOut, short iCharSet)
{
	// Set attributes for font display


	m_lfStyle.lfHeight = lPoints;
	m_lfStyle.lfWidth = 0;
	m_lfStyle.lfEscapement = 0;
	m_lfStyle.lfOrientation = 0;
	m_lfStyle.lfWeight = lWeight;
	m_lfStyle.lfItalic = bItalic;
	m_lfStyle.lfUnderline = bUnderline;
	m_lfStyle.lfStrikeOut  = bStrikeOut;
	m_lfStyle.lfCharSet = iCharSet;
	m_lfStyle.lfOutPrecision = OUT_DEFAULT_PRECIS;
	m_lfStyle.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	m_lfStyle.lfQuality = DEFAULT_QUALITY;
	m_lfStyle.lfPitchAndFamily = DEFAULT_PITCH;

	 _tcscpy (m_lfStyle.lfFaceName, sFaceName);

	return S_OK;
}

STDMETHODIMP CGame::TextDisplay(BSTR bstrString, int lX, int lY, BOOL bTransparent, BOOL bDrawBackground)
{
	// Display text using selected color, font attribs
	BYTE iMode;
	if (bTransparent)
		iMode = TRANSPARENT;
	else
		iMode = OPAQUE;

	HFONT hFont, hOldFont;
	
	hFont = CreateFontIndirect(&m_lfStyle);
	if (hFont == NULL)
		Error(_T("TextDisplay failed on call to CreateFontIndirect"));


	hOldFont = (HFONT) SelectObject(m_hdc, hFont);
	SetTextColor(m_hdc, RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
    SetBkMode(m_hdc, iMode);
	ExtTextOut(m_hdc, lX, lY, 0, NULL, bstrString, wcslen(bstrString), NULL);
	SelectObject(m_hdc, hOldFont);
	DeleteObject(hFont);

	if (bDrawBackground) {
		hFont = CreateFontIndirect(&m_lfStyle);
		if (hFont == NULL)
			Error(_T("TextDisplay failed on call to CreateFontIndirect"));

	    hOldFont = (HFONT) SelectObject(m_sprBackground->GetSourceDC(), hFont);
		SetTextColor(m_sprBackground->GetSourceDC(), RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
		SetBkMode(m_sprBackground->GetSourceDC(), iMode);
		ExtTextOut(m_sprBackground->GetSourceDC(), lX, lY, 0, NULL, bstrString, wcslen(bstrString), NULL);
		SelectObject(m_sprBackground->GetSourceDC(), hOldFont);
		DeleteObject(hFont);
	}
	return S_OK;
}

STDMETHODIMP CGame::TextGetDimensions(BSTR sText, VARIANT *width, VARIANT *height)
{
	// returns the height and width of a string for selected font 
	SIZE szSize;

	HFONT hFont, hOldFont;
	
	hFont = CreateFontIndirect(&m_lfStyle);
	if (hFont == NULL)
		Error(_T("TextDisplay failed on call to CreateFontIndirect"));

	hOldFont = (HFONT) SelectObject(m_hdc, hFont);

	GetTextExtentPoint32(m_hdc, sText, lstrlen(sText), &szSize);

	CComVariant vWidth(*width), vHeight(*height);

	vWidth.ChangeType(VT_I2);
	vWidth.iVal = szSize.cx;
	vWidth.Detach(width);

	vHeight.ChangeType(VT_I2);
	vHeight.iVal = szSize.cy;
	vHeight.Detach(height);
	
	SelectObject(m_hdc, hOldFont);
	DeleteObject(hFont);

	return S_OK;
}


STDMETHODIMP CGame::TileSetMap(long lSpriteNum, VARIANT*  arrMap, VARIANT*  arrCollisionTiles)
{
	// draws a tiled map on the background, using a sprite's frames for tiles.
	// the map is contained in arrMap, and collision tiles are listed in arrCollisionTiles

		if (m_sprDynamic[lSpriteNum]==NULL)
			return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

		
		long lLBound1,lLBound2,lUBound1,lUBound2;

        // *********
		// ** first get arrMap - the tile Map
        // *********
		
		SAFEARRAY ** psaMap;
		if (arrMap->vt==(VT_BYREF | VT_ARRAY| VT_VARIANT) ||
			arrMap->vt==(VT_ARRAY| VT_VARIANT)) { 

			// create a safearray
			psaMap = arrMap->pparray;
		
			long lDims = SafeArrayGetDim(*psaMap);
			if (lDims!=2) 
				return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrMap must be a 2-dimensional array", IID_IGame, 0);

			SafeArrayGetLBound(*psaMap,1,&lLBound1);    
			SafeArrayGetUBound(*psaMap,1,&lUBound1);    
			SafeArrayGetLBound(*psaMap,2,&lLBound2);
			SafeArrayGetUBound(*psaMap,2,&lUBound2);
			
			if (lLBound1!=0 || lLBound2!=0)
				return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrMap bounds must start at 0 in both dimensions.", IID_IGame, 0);

			if (lUBound1>MAX_TILES_X || lUBound2>MAX_TILES_Y)
				return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrMap contains a map that is too large.", IID_IGame, 0);

			m_iNumTilesX = lUBound2;
			m_iNumTilesY = lUBound1;


			HRESULT hr;
			void * pData;
			hr = SafeArrayAccessData( *psaMap, &pData );
			if( FAILED(hr) )
				return hr;
			VARIANT * vData = static_cast<VARIANT*>(pData);

			int xx,yy;
			int iOffset=0;
			for (xx=lLBound2;xx<=lUBound2;xx++) 
				for (yy=lLBound1;yy<=lUBound1;yy++) {
					m_iTiles[yy][xx] = vData[iOffset].iVal;
					iOffset++;
				}

			// release array contents
			SafeArrayUnaccessData(*psaMap);


			
		} else
			return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrMap must be a 2-dimensional array of frame values.", IID_IGame, 0);



        // *********
		// ** next get arrCollisionTiles - the array of collision tiles
        // *********
		TileSetCollisionTiles(arrCollisionTiles);


        // *********
		// ** display the map
        // *********
		int iCurrX = 0, iCurrY = 0;
		for (int yy=lLBound1;yy<m_iNumTilesY;yy++) {
			for (int xx=lLBound2;xx<m_iNumTilesX;xx++) {
				m_sprDynamic[lSpriteNum]->SetFrame(m_iTiles[yy][xx]);
				DrawBackgroundSprite(lSpriteNum, iCurrX, iCurrY);
				iCurrX += m_sprDynamic[lSpriteNum]->ImageWidth();
			}
			iCurrY += m_sprDynamic[lSpriteNum]->ImageHeight();
			iCurrX = 0;
		}

	m_bCheckTileCollisions = TRUE;

	m_iTileWidth = m_sprDynamic[lSpriteNum]->ImageWidth();
	m_iTileHeight = m_sprDynamic[lSpriteNum]->ImageHeight();

	m_iScrollOffsetX=0; m_iScrollOffsetY=0;

	m_lSpriteNumTiles = lSpriteNum;
	return S_OK;
}

STDMETHODIMP CGame::SpriteDelete(long lSpriteNum)
{
	// remove a sprite from list of all
	if (m_sprDynamic[lSpriteNum]!=NULL) {
		delete m_sprDynamic[lSpriteNum];
		m_sprDynamic[lSpriteNum] = NULL;
	}
	return S_OK;
}

STDMETHODIMP CGame::get_TileAtPosition(int iXMapOffset, int iYMapOffset, short *pVal)
{
	// allow read access to the Tile map
	if (!m_bCheckTileCollisions)
		return AtlReportError(GetObjectCLSID(), "TileAtPosition: you have not defined a tiled map (use DrawSetTileMap).", IID_IGame, 0);
	
	if (iXMapOffset > m_iNumTilesX || iYMapOffset > m_iNumTilesY)
		return AtlReportError(GetObjectCLSID(), "TileAtPosition: x,y map position is out of bounds..", IID_IGame, 0);

	*pVal = m_iTiles[iYMapOffset][iXMapOffset];	

	return S_OK;
}

STDMETHODIMP CGame::put_TileAtPosition(int iXMapOffset, int iYMapOffset, short newVal)
{
	// allow write access to the Tile map
	if (!m_bCheckTileCollisions)
		return AtlReportError(GetObjectCLSID(), "TileAtPosition: you have not defined a tiled map (use DrawSetTileMap).", IID_IGame, 0);

	if (iXMapOffset > m_iNumTilesX || iYMapOffset > m_iNumTilesY)
		return AtlReportError(GetObjectCLSID(), "TileAtPosition: x,y map position is out of bounds..", IID_IGame, 0);

	if (m_lSpriteNumTiles < 0)
		return AtlReportError(GetObjectCLSID(), "TileAtPosition: You must first call TileSetMap to define a tile map", IID_IGame, 0);

	m_iTiles[iYMapOffset][iXMapOffset] = newVal;

	m_sprDynamic[m_lSpriteNumTiles]->SetFrame(newVal);
	DrawBackgroundSprite(m_lSpriteNumTiles, iXMapOffset*m_sprDynamic[m_lSpriteNumTiles]->ImageWidth(), iYMapOffset*m_sprDynamic[m_lSpriteNumTiles]->ImageHeight());

	
	return S_OK;
}


STDMETHODIMP CGame::get_TileAtPositionXY(int lX, int lY, short *pVal)
{
	// TODO: Add your implementation code here
	
	if (!m_bCheckTileCollisions)
		return AtlReportError(GetObjectCLSID(), "TileAtPositionXY: you have not defined a tiled map yet.", IID_IGame, 0);
	
	int iXLoc = lX/m_iTileWidth, iYLoc = lY/m_iTileHeight;

	*pVal = m_iTiles[iYLoc][iXLoc];
	return S_OK;
}

STDMETHODIMP CGame::get_TileCausesCollision(int iTileNum, BOOL *pVal)
{
	// returns TRUE if a tile number causes collision events
	BOOL bRetVal = FALSE;

	for (int i=0; i<=m_iNumTilesCollision; i++) {
		if (m_iTilesCollision[i] == iTileNum) {
			bRetVal = TRUE;
			break;
		}
	}
	*pVal = bRetVal;

	return S_OK;
}

STDMETHODIMP CGame::SpriteDiminishCollisionRect(long lSpriteNum, int iDiminishX, int iDiminishY)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteDiminishCollisionRect: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

		// Adjust the collision rectangle for a sprite
	m_sprDynamic[lSpriteNum]->SetShrinkX(iDiminishX);
	m_sprDynamic[lSpriteNum]->SetShrinkY(iDiminishY);

	return S_OK;
}


STDMETHODIMP CGame::get_TileSpriteCanMove(long lSpriteNum, int iAngle, int iVelocity, BOOL *pVal)
{
	// determines if a sprite can move a specified X,Y distance through the tiled world..

	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "TileSpriteCanMove: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// if sprite tile collisions are off, return TRUE
	if (!m_sprDynamic[lSpriteNum]->GetTileCollisionsOn()) {
		*pVal = TRUE;
		return S_OK;
	}

	
	float fRads = iAngle* 0.0174532925199433;
	float iXDistance = ((float) iVelocity*cos(fRads));
	float iYDistance = ((float) iVelocity*sin(fRads));

	VARIANT vX1, vY1, vX2, vY2, vX3, vY3;
	int iOffSet1=-1, iOffSet2=-1, iOffSet3=-1;

	if (iXDistance >= 1 && iYDistance >= 1) {
		// SOUTHEAST
		iOffSet1 = 0;
		iOffSet2 = 1;
		iOffSet3 = 2;
	} else
	if (iXDistance <= -1 && iYDistance >= 1) {
		// SOUTHWEST
		iOffSet1 = 2;
		iOffSet2 = 3;
		iOffSet3 = 4;
	} else
	if (iXDistance <= -1 && iYDistance <= -1) {
		// NORTHWEST
		iOffSet1 = 4;
		iOffSet2 = 5;
		iOffSet3 = 6;
	} else
	if (iXDistance >= 1 && iYDistance <= -1) {
		// NORTHEAST
		iOffSet1 = 6;
		iOffSet2 = 7;
		iOffSet3 = 0;
	} else 
	if (iXDistance >= 1) {
		// EAST
		iOffSet1 = 7;
		iOffSet2 = 1;
	} else
	if (iYDistance >= 1) {
		// SOUTH
		iOffSet1 = 3;
		iOffSet2 = 1;
	} else
	if (iXDistance <= -1) {
		// WEST
		iOffSet1 = 5;
		iOffSet2 = 3;
	} else
	if (iYDistance <= -1) {
		// NORTH
		iOffSet1 = 5;
		iOffSet2 = 7;
	} 

	if (iOffSet1 == -1 && iOffSet2 == -1 && iOffSet3 == -1) {
		// no movement specified, return TRUE
		*pVal = TRUE;
		return S_OK;
	}
	
	VariantInit(&vX1);
	VariantInit(&vY1);
	VariantInit(&vX2);
	VariantInit(&vY2);
	VariantInit(&vX3);
	VariantInit(&vY3);

	SpriteGetPositionEx(lSpriteNum, iOffSet1, &vX1, &vY1);
	SpriteGetPositionEx(lSpriteNum, iOffSet2, &vX2, &vY2);
	if (iOffSet3 >=0) SpriteGetPositionEx(lSpriteNum, iOffSet3, &vX3, &vY3);

	vX1.iVal = vX1.iVal + m_iScrollOffsetX;
	vX2.iVal = vX2.iVal + m_iScrollOffsetX;
	vX3.iVal = vX3.iVal + m_iScrollOffsetX;
	vY1.iVal = vY1.iVal + m_iScrollOffsetY;
	vY2.iVal = vY2.iVal + m_iScrollOffsetY;
	vY3.iVal = vY3.iVal + m_iScrollOffsetY;

	short iTile1, iTile2, iTile3;
	BOOL bCausesCollision1, bCausesCollision2, bCausesCollision3;
	get_TileAtPositionXY(vX1.iVal + iXDistance, vY1.iVal + iYDistance, &iTile1);
	get_TileCausesCollision(iTile1, &bCausesCollision1);
	get_TileAtPositionXY(vX2.iVal + iXDistance, vY2.iVal + iYDistance, &iTile2);
	get_TileCausesCollision(iTile2, &bCausesCollision2);
	if (iOffSet3 >=0) {
		get_TileAtPositionXY(vX3.iVal + iXDistance, vY3.iVal + iYDistance, &iTile3);
		get_TileCausesCollision(iTile3, &bCausesCollision3);
	}

    if (bCausesCollision1 || bCausesCollision2 || (iOffSet3>0 && bCausesCollision3)) {
		*pVal = FALSE;
		if (m_bTileExtendedCollisions) {
			m_bTileNumCollisions = 0;
			if (bCausesCollision1) {
				m_bTileCollisionsX[m_bTileNumCollisions] = (vX1.iVal + iXDistance)/m_iTileWidth;
				m_bTileCollisionsY[m_bTileNumCollisions] = (vY1.iVal + iYDistance)/m_iTileHeight;
				m_bTileCollisionsTileNum[m_bTileNumCollisions] = iTile1;
				m_bTileNumCollisions++;
			}
			if (bCausesCollision2) {
				m_bTileCollisionsX[m_bTileNumCollisions] = (vX2.iVal + iXDistance)/m_iTileWidth;
				m_bTileCollisionsY[m_bTileNumCollisions] = (vY2.iVal + iYDistance)/m_iTileHeight;
				m_bTileCollisionsTileNum[m_bTileNumCollisions] = iTile2;
				m_bTileNumCollisions++;
			}
			if (iOffSet3>0 && bCausesCollision3) {
				m_bTileCollisionsX[m_bTileNumCollisions] = (vX3.iVal + iXDistance)/m_iTileWidth;
				m_bTileCollisionsY[m_bTileNumCollisions] = (vY3.iVal + iYDistance)/m_iTileHeight;
				m_bTileCollisionsTileNum[m_bTileNumCollisions] = iTile3;
				m_bTileNumCollisions++;
			}
		}
	}
	else
		*pVal = TRUE;

	VariantClear(&vX1);
	VariantClear(&vY1);
	VariantClear(&vX2);
	VariantClear(&vY2);
	VariantClear(&vX3);
	VariantClear(&vY3);
	
	return S_OK;
}

STDMETHODIMP CGame::DrawGetDimensions(VARIANT *width, VARIANT *height)
{
	CComVariant lWidth(*width), lHeight(*height);


	lWidth.ChangeType(VT_I2);
	lWidth.iVal = m_iBoundsWidth;
	lWidth.Detach(width);

	lHeight.ChangeType(VT_I2);
	lHeight.iVal = m_iBoundsHeight;
	lHeight.Detach(height);

	return S_OK;
}

STDMETHODIMP CGame::SpriteGetPosition(long lSpriteNum, VARIANT *lX, VARIANT *lY)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteGetPosition: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// TODO: Add your implementation code here
	CComVariant lXPos(*lX), lYPos(*lY);

	lXPos.ChangeType(VT_I2);
	lXPos.iVal = m_sprDynamic[lSpriteNum]->GetX();
	lXPos.Detach(lX);

	lYPos.ChangeType(VT_I2);
	lYPos.iVal = m_sprDynamic[lSpriteNum]->GetY();
	lYPos.Detach(lY);

	return S_OK;
}

STDMETHODIMP CGame::get_SpriteActive(long lSpriteNum, BOOL *pVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteActive: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// activation value of sprite
	*pVal = m_sprDynamic[lSpriteNum]->IsAlive();
	return S_OK;
}

STDMETHODIMP CGame::put_SpriteActive(long lSpriteNum, BOOL newVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteActive: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// activate/deactivate a sprite
	m_sprDynamic[lSpriteNum]->SetAlive(newVal);

	if (newVal) m_sprDynamic[lSpriteNum]->SetShouldErase(TRUE);

	return S_OK;
}

STDMETHODIMP CGame::SpriteGetPositionEx(long lSpriteNum, int iOffset, VARIANT *lX, VARIANT *lY)
{
	// get the position of a specific part of the sprite
	// where iOffset is the part corresponding to a direction
	// constant 0 thru 7, e.g. 0 will return middle right of 
	// sprite... 
	// *NOTE: also includes COLLISION rectangle adjustment, so
	//       is useful for collision detection with tiles!
	// ** DOES NOT INCLUDE offset in calc!
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteGetPositionEx: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	int x1, y1;
	switch (iOffset) {
		case 0:
			// east
			x1 = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->ImageWidth() - m_sprDynamic[lSpriteNum]->GetShrinkX();
			y1 = m_sprDynamic[lSpriteNum]->GetY() + (m_sprDynamic[lSpriteNum]->ImageHeight()/2);
			break;
		case 1:
			// southeast
			x1 = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->ImageWidth() - m_sprDynamic[lSpriteNum]->GetShrinkX();
			y1 = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->ImageHeight() - m_sprDynamic[lSpriteNum]->GetShrinkY();
			break;
		case 2:
			// south
			x1 = m_sprDynamic[lSpriteNum]->GetX() + (m_sprDynamic[lSpriteNum]->ImageWidth()/2);
			y1 = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->ImageHeight() - m_sprDynamic[lSpriteNum]->GetShrinkY();
			break;
		case 3:
			// southwest
			x1 = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->GetShrinkX();
			y1 = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->ImageHeight() - m_sprDynamic[lSpriteNum]->GetShrinkY();
			break;
		case 4:
			// west
			x1 = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->GetShrinkX();
			y1 = m_sprDynamic[lSpriteNum]->GetY() + (m_sprDynamic[lSpriteNum]->ImageHeight()/2);
			break;
		case 5:
			// northwest
			x1 = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->GetShrinkX();
			y1 = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->GetShrinkY();
			break;
		case 6:
			// north
			x1 = m_sprDynamic[lSpriteNum]->GetX() + (m_sprDynamic[lSpriteNum]->ImageWidth()/2);
			y1 = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->GetShrinkY();
			break;
		case 7:
			// northeast
			x1 = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->ImageWidth() - m_sprDynamic[lSpriteNum]->GetShrinkX();
			y1 = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->GetShrinkY();
			break;
		case 8:
			// center
			x1 = m_sprDynamic[lSpriteNum]->GetX() + (m_sprDynamic[lSpriteNum]->ImageWidth()/2);
			y1 = m_sprDynamic[lSpriteNum]->GetY() + (m_sprDynamic[lSpriteNum]->ImageHeight()/2);
			break;

		default:
			LPCSTR lpcsError=(const char *) malloc(150); //calloc(150, sizeof(char));;
			sprintf( (char *)lpcsError, "SpriteGetPositionEx: iOffset parm must be 0 to 8; received %d", iOffset);
			return AtlReportError(GetObjectCLSID(), lpcsError, IID_IGame, 0);

	}

	CComVariant lXPos(*lX), lYPos(*lY);

	lXPos.ChangeType(VT_I2);
	lXPos.iVal =x1;
	lXPos.Detach(lX);

	lYPos.ChangeType(VT_I2);
	lYPos.iVal =y1;
	lYPos.Detach(lY);

	return S_OK;
}


STDMETHODIMP CGame::get_SpriteFrame(long lSpriteNum, int *pVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteFrame: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// TODO: Add your implementation code here
	*pVal = m_sprDynamic[lSpriteNum]->GetFrame();
	return S_OK;
}

STDMETHODIMP CGame::put_SpriteFrame(long lSpriteNum, int newVal)
{
	// TODO: Add your implementation code here
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteFrame: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// destroy any frame range
	m_sprDynamic[lSpriteNum]->SetFirstFrame(-1);
	m_sprDynamic[lSpriteNum]->SetLastFrame(-1);

	m_sprDynamic[lSpriteNum]->SetFrame(newVal);

	return S_OK;
}



STDMETHODIMP CGame::get_UtilRandom(long lMinVal, long lMaxVal, long *pVal)
{
	// generate a random number between minval and maxval
	*pVal = (rand() % (lMaxVal-lMinVal+1)) + lMinVal;

	return S_OK;
}

STDMETHODIMP CGame::UtilMemoryStatus(VARIANT *iMemoryLoad, VARIANT *lTotalPhysicalBytes, VARIANT *lAvailablePhysicalBytes, VARIANT *lTotalVirtualBytes, VARIANT *lAvailableVirtualBytes)
{
	// get info on memory utilization
	CComVariant vMemoryLoad(*iMemoryLoad), 
		vTotalPhysicalBytes(*lTotalPhysicalBytes), 
		vAvailablePhysicalBytes(*lAvailablePhysicalBytes),
		vTotalVirtualBytes(*lTotalVirtualBytes), 
		vAvailableVirtualBytes(*lAvailableVirtualBytes);
	
	MEMORYSTATUS memstat;

	GlobalMemoryStatus(&memstat);


	vMemoryLoad.ChangeType(VT_I2);
	vMemoryLoad.iVal = memstat.dwMemoryLoad;
	vMemoryLoad.Detach(iMemoryLoad);

	vTotalPhysicalBytes.ChangeType(VT_I4);
	vTotalPhysicalBytes.lVal = memstat.dwTotalPhys;
	vTotalPhysicalBytes.Detach(lTotalPhysicalBytes);

	vAvailablePhysicalBytes.ChangeType(VT_I4);
	vAvailablePhysicalBytes.lVal = memstat.dwAvailPhys;
	vAvailablePhysicalBytes.Detach(lAvailablePhysicalBytes);

	vTotalVirtualBytes.ChangeType(VT_I4);
	vTotalVirtualBytes.lVal = memstat.dwTotalVirtual;
	vTotalVirtualBytes.Detach(lTotalVirtualBytes);

	vAvailableVirtualBytes.ChangeType(VT_I4);
	vAvailableVirtualBytes.lVal = memstat.dwAvailVirtual;
	vAvailableVirtualBytes.Detach(lAvailableVirtualBytes);

	return S_OK;
}

STDMETHODIMP CGame::get_SpriteOnScreen(long lSpriteNum, BOOL *pVal)
{
	// returns TRUE if sprite is visible on screen else FALSE
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteOnScreen: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	RECT rect1, rect2, rectDest;

	rect1.left = m_sprDynamic[lSpriteNum]->GetX();
	rect1.top = m_sprDynamic[lSpriteNum]->GetY();
	rect1.right = m_sprDynamic[lSpriteNum]->GetX() + m_sprDynamic[lSpriteNum]->ImageWidth();
	rect1.bottom = m_sprDynamic[lSpriteNum]->GetY() + m_sprDynamic[lSpriteNum]->ImageHeight();

	rect2.left = 0;
	rect2.top = 0;
	rect2.right = m_iBoundsWidth;
	rect2.bottom = m_iBoundsHeight;

	if (IntersectRect(&rectDest, &rect1, &rect2))
        *pVal = TRUE;
    else
		*pVal = FALSE;
	return S_OK;
}

STDMETHODIMP CGame::get_SpriteZOrder(long lSpriteNum, short *pVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteZOrder: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	// TODO: Add your implementation code here
	*pVal = m_sprDynamic[lSpriteNum]->GetZOrder();
	return S_OK;
}

STDMETHODIMP CGame::put_SpriteZOrder(long lSpriteNum, short newVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteZOrder: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	// maintain Z Order
	// max is 32000
	if (newVal > 32000) newVal = 32000;

	m_sprDynamic[lSpriteNum]->SetZOrder(newVal);
	DoZorder();

	return S_OK;
}

long lHoldOrder[MAX_DYNAMIC_SPRITES];

int __cdecl CGame::ZOrderCompare(const void *arg1, const void *arg2)
{
	// used for the qsort call in DoZOrder to determine sort
	long lArg1 = *((long*)arg1), lArg2 = *((long*)arg2);
	short lZOrder1,lZOrder2;
	
	lZOrder1 = lHoldOrder[lArg1];
	lZOrder2 = lHoldOrder[lArg2];

	if (lZOrder1 == lZOrder2)
		return 0;
	else
	if (lZOrder1 < lZOrder2)
		return -1;
	else
		return 1;
}

void CGame::DoZorder()
{
	// refresh the display order of sprites based on their zorder
	
	// first fill order array in sequence of sprite
	for (int i=0; i<MAX_DYNAMIC_SPRITES; i++) {
		m_lDisplayOrder[i] = i;
		if (m_sprDynamic[i] == NULL)
			lHoldOrder[i] = 32767;
		else
			lHoldOrder[i] = m_sprDynamic[i]->GetZOrder();;
	}

	// now quicksort based on zorder

	qsort((void *) &m_lDisplayOrder, MAX_DYNAMIC_SPRITES, sizeof(long),  ZOrderCompare);
	
	return;
}



STDMETHODIMP CGame::TimerDefineCustom(int iTimerNum, int iDelay, BOOL bRepeat)
{
	// TODO: Add your implementation code here
	if (iTimerNum > (MAX_TIMERS - 1))
		return AtlReportError(GetObjectCLSID(), "TimerDefineCustom: parm iTimerNum is too high.", IID_IGame, 0);

	if (m_tmrCustom[iTimerNum] != NULL) {
		delete m_tmrCustom[iTimerNum];
		m_tmrCustom[iTimerNum] = NULL;
	}

	m_tmrCustom[iTimerNum] = new CTimerCustom();
	m_tmrCustom[iTimerNum]->SetDelay(iDelay);
	m_tmrCustom[iTimerNum]->SetRepeat(bRepeat);
	m_tmrCustom[iTimerNum]->SetValue(iDelay);

	if (iTimerNum>m_iNumTimers) m_iNumTimers=iTimerNum;

	return S_OK;
}

STDMETHODIMP CGame::get_InputFireMouseCollisions(BOOL *pVal)
{
	*pVal = m_bFireMouseCollisions ;
	return S_OK;
}

STDMETHODIMP CGame::put_InputFireMouseCollisions(BOOL newVal)
{

	m_bFireMouseCollisions = newVal;
	return S_OK;
}

STDMETHODIMP CGame::SoundPlayLoop(long lSoundNum)
{

	if (sndWaves[lSoundNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SoundPlayLoop: You have not defined that sound yet. Check lSoundNum parameter.", IID_IGame, 0);

	sndWaves[lSoundNum]->SetRepeat(TRUE);
	sndWaves[lSoundNum]->Play();

	return S_OK;
}

STDMETHODIMP CGame::SoundStopLoop(long lSoundNum)
{
	if (sndWaves[lSoundNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SoundStopLoop: You have not defined that sound yet. Check lSoundNum parameter.", IID_IGame, 0);

	sndWaves[lSoundNum]->SetRepeat(FALSE);
	sndWaves[lSoundNum]->Stop();
	return S_OK;
}

STDMETHODIMP CGame::get_SpriteTileCollisionsOn(long lSpriteNum, BOOL *pVal)
{
	// tile collisions property, if ON (default) then sprite causes tile collisions
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteTileCollisionsOn: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	
	*pVal = m_sprDynamic[lSpriteNum]->GetTileCollisionsOn();

	return S_OK;
}

STDMETHODIMP CGame::put_SpriteTileCollisionsOn(long lSpriteNum, BOOL newVal)
{

	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteTileCollisionsOn: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	
	m_sprDynamic[lSpriteNum]->SetTileCollisionsOn(newVal);

	return S_OK;
}



STDMETHODIMP CGame::SoundDelete(long lSoundNum)
{
	// TODO: Add your implementation code here
	if (sndWaves[lSoundNum]!=NULL) {
		delete sndWaves[lSoundNum];
		sndWaves[lSoundNum] = NULL;
	}

	return S_OK;
}





void HRVERIFY(HRESULT hr, char * msg)
{
  if(FAILED(hr)) {
     TCHAR buf[1024];
     _stprintf(buf, _T("The ASpriteCE Scripting methods encountered an unexpected error while executing script: %08lx, %s"), hr, msg);
     ::MessageBox(NULL, buf, _T(""), MB_SETFOREGROUND);
  }

}

// Script Engine CLSIDs...
#include <initguid.h>
DEFINE_GUID(CLSID_VBScript, 0xb54f3741, 0x5b07, 0x11cf, 0xa4, 0xb0, 0x0,
           0xaa, 0x0, 0x4a, 0x55, 0xe8);


STDMETHODIMP CGame::ScriptExecute(BSTR sScript)
{


   if (m_pMySite != NULL) {
	   // Delete site & objects...
	   delete m_pMySite;
	   m_pMySite = NULL;
   }
   if (m_pAS != NULL) {
	   m_pAS->Close();
	   m_pASP->Release();
	   m_pAS->Release();
	   m_pAS = NULL;
   }


  // Instantiate script site & objects...
   m_pMySite = new CMyScriptSite;
   m_pMySite->SetParent(m_this, &m_hwnd);

   
   // Register your type-library
   ITypeLib *ptLib = 0;
   HRVERIFY(LoadTypeLib(L"aspritece.dll", &ptLib), "LoadTypeLib");

   // Initialize your IActiveScriptSite implementation with your
   // object's ITypeInfo...
   ptLib->GetTypeInfoOfGuid(CLSID_Game, &m_pMySite->m_pTypeInfo);
   ptLib->Release();

   // Initialize your IActiveScriptSite implementation with your
   // script object's IUnknown interface...
   HRVERIFY(this->QueryInterface(IID_IUnknown,
	  (void **)&m_pMySite->m_pUnkScriptObject), "IUnknown initialization");


   // Start inproc script engine, VBSCRIPT.DLL
	HRVERIFY(CoCreateInstance(CLSID_VBScript, NULL, CLSCTX_INPROC_SERVER,
	  IID_IActiveScript, (void **)&m_pAS), 
	  "CoCreateInstance() for CLSID_VBScript");

   // Get engine's IActiveScriptParse interface.
   HRVERIFY(m_pAS->QueryInterface(IID_IActiveScriptParse, (void **)&m_pASP),
	  "QueryInterface() for IID_IActiveScriptParse");

   // Give engine your IActiveScriptSite interface...
   HRVERIFY(m_pAS->SetScriptSite((IActiveScriptSite *)m_pMySite),
	  "IActiveScript::SetScriptSite()");


   // Give the engine a chance to initialize itself...
   HRVERIFY(m_pASP->InitNew(), "IActiveScriptParse::InitNew()");

   // Add a root-level item to the engine's name space...
   HRVERIFY(m_pAS->AddNamedItem(L"Game", SCRIPTITEM_ISVISIBLE |
	  SCRIPTITEM_ISSOURCE), "IActiveScript::AddNamedItem()");

	   // Parse the code scriptlet...
   EXCEPINFO ei;
   BSTR pParseText = SysAllocString(sScript);
   HRESULT hr = m_pASP->ParseScriptText(pParseText, L"Game", NULL, NULL, 0,
      0, 0L, NULL, &ei);
   SysFreeString(pParseText);
   
   // Set the engine state. This line actually triggers the execution
   // of the script.
   if (!FAILED(hr))
	m_pAS->SetScriptState(SCRIPTSTATE_CONNECTED);
   
   

   
   return S_OK;
}





STDMETHODIMP CGame::UtilSleep(long lMilliseconds)
{
	Sleep(lMilliseconds);

	return S_OK;
}

STDMETHODIMP CGame::ScriptStop()
{
	// TODO: Add your implementation code here
	// Close script and release interfaces...

	/*
	EXCEPINFO ei;
	m_pAS->InterruptScriptThread(SCRIPTTHREADID_ALL, &ei, 0);

	m_pAS->Close();
	m_pASP->Release();
	m_pAS->Release();

	// Delete site & objects...
	delete m_pMySite;
	m_pMySite = NULL;
	m_pAS = NULL;
	*/

	if (m_pAS!=NULL) {
		EXCEPINFO ei;
		m_pAS->InterruptScriptThread(SCRIPTTHREADID_ALL, &ei, 0);
	}
	return S_OK;
}

STDMETHODIMP CGame::get_AppPath(BSTR *pVal)
{
	// TODO: Add your implementation code here
	*pVal = SysAllocString(m_sAppPath);
	return S_OK;
}

STDMETHODIMP CGame::put_AppPath(BSTR newVal)
{
	// TODO: Add your implementation code here
	m_sAppPath = SysAllocString(newVal);
	return S_OK;
}

STDMETHODIMP CGame::get_DrawHwnd(DWORD *pVal)
{
	// use this property in lieu of DrawInit parm 
	// when necessary for scripting
	*pVal = (DWORD) m_hwnd;
	return S_OK;
}

STDMETHODIMP CGame::put_DrawHwnd(DWORD newVal)
{
	m_hwnd = (HWND) newVal;

	return S_OK;
}

STDMETHODIMP CGame::SpriteStretch(long lSpriteNum, long lWidth, long lHeight)
{
	// set the new stretched width and height here
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteStretch: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	
	if (lWidth <= 0 && lHeight <= 0)
		// turn off stretching
		m_sprDynamic[lSpriteNum]->SetStretch(FALSE, 0, 0);
	else
		// enable stretching
		m_sprDynamic[lSpriteNum]->SetStretch(TRUE, lWidth, lHeight);

	return S_OK;
}

STDMETHODIMP CGame::ScriptRaiseMessage(BSTR sMessage)
{
	// Raise a message to the container from the script
	Fire_ScriptMessage(sMessage);

	return S_OK;
}

STDMETHODIMP CGame::DoInit()
{
	// TODO: Add your implementation code here
	m_bNagged = FALSE;

	m_lNumSprites = 0;
	m_lNumSounds = 0;
	m_bInputKeyUp = FALSE;
	m_bInputKeyDown = FALSE;
	m_bInputKeyLeft = FALSE;
	m_bInputKeyRight = FALSE;
	m_bInputKeyA = FALSE;
	m_bInputKeyB = FALSE;
	m_bInputKeyC = FALSE;
	m_bInputKeyStart = FALSE;
	m_bInputKeyAux1 = FALSE;
	m_bInputKeyAux2 = FALSE;

	m_bAutoFlipBackBuffer = TRUE;

	m_bPaused = FALSE;

	
	m_iScrollOffsetX=0;
	m_iScrollOffsetY=0;

	m_bCheckTileCollisions = FALSE;
	m_iNumTilesX = 0;
	m_iNumTilesY = 0;
	m_iNumTilesCollision = 0;
	m_iTileWidth = 0;
	m_iTileHeight = 0;
	m_DrawRed=0, m_DrawGreen=0, m_DrawBlue=0;
	m_DrawBackRed=0; m_DrawBackGreen=0; m_DrawBackBlue=0;
	m_DrawTransRed=0; m_DrawTransGreen=0; m_DrawTransBlue=0;

	m_lTimeLastFlipBackBuffer=1;
	m_lLastFrameTime=1;	

	m_iNumTimers = 0;
	m_bFireMouseCollisions = TRUE;

	m_MouseX=-1; m_MouseY=-1;

	m_bNeedFullScreenRefresh=FALSE;

	m_lSpriteNumTiles = -1;

	m_bInitialized = FALSE;

	m_bTileExtendedCollisions = FALSE;
	m_bTileNumCollisions = 0;

	return S_OK;
}

STDMETHODIMP CGame::DrawLine(int x1, int y1, int x2, int y2, int iWidth, BOOL bDrawBackground)
{
	
	POINT ptLine[2];
	POINT *pptLine = ptLine;
	int iLineStyle;
	ptLine[0].x = x1; ptLine[0].y = y1;
	ptLine[1].x = x2; ptLine[1].y = y2;
	
	HDC hDC;                  // Handle to the display device context 
	HPEN hPen,            // Handle to the new brush object 
		 hOldPen;         // Handle to the old brush object 

	// Retrieve the handle to the display device context.
	hDC = m_hdc;
	hPen = CreatePen (PS_SOLID, iWidth, RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
	if (hOldPen = (HPEN) SelectObject (hDC, hPen))
	{
		BOOL bRet;
		// Draws a line.
		bRet = Polyline(hDC, pptLine, 2);
		// Select the old brush into the device context.
		SelectObject (hDC, hOldPen);
		// Delete the brush object and free all resources associated with it.
		DeleteObject (hPen);
	}

	if (bDrawBackground) {
		// Retrieve the handle to the display device context.
		hDC = m_sprBackground->GetSourceDC();
		hPen = CreatePen (PS_SOLID, iWidth, RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
		if (hOldPen = (HPEN) SelectObject (hDC, hPen))
		{
			BOOL bRet;
			// Draws a line.
			bRet = Polyline(hDC, pptLine, 2);
			// Select the old brush into the device context.
			SelectObject (hDC, hOldPen);
			// Delete the brush object and free all resources associated with it.
			DeleteObject (hPen);
		}
	}

	return S_OK;
}

STDMETHODIMP CGame::DrawRectangle(int x1, int y1, int x2, int y2, BOOL bDrawBackground)
{

	HDC hDC;                  // Handle to the display device context 
	HBRUSH hBrush,            // Handle to the new brush object 
		 hOldBrush;         // Handle to the old brush object 

	// Retrieve the handle to the display device context.
	hDC = m_hdc;
	// Create a solid brush object and select it into the device context.
	hBrush = CreateSolidBrush (RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
	if (hOldBrush = (HBRUSH) SelectObject (hDC, hBrush))
	{
		BOOL bRet;
		// Draws a rectangle.
		bRet = Rectangle (hDC, x1, y1, x2, y2);
		// Select the old brush into the device context.
		SelectObject (hDC, hOldBrush);
		// Delete the brush object and free all resources associated with it.
		DeleteObject (hBrush);
	}
  
	if (bDrawBackground) {
		// Retrieve the handle to the display device context.
		hDC = m_sprBackground->GetSourceDC();
		// Create a solid brush object and select it into the device context.
		hBrush = CreateSolidBrush (RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
		if (hOldBrush = (HBRUSH) SelectObject (hDC, hBrush))
		{
			BOOL bRet;
			// Draws a rectangle.
			bRet = Rectangle (hDC, x1, y1, x2, y2);
			// Select the old brush into the device context.
			SelectObject (hDC, hOldBrush);
			// Delete the brush object and free all resources associated with it.
			DeleteObject (hBrush);
		}
	}

	return S_OK;
}

STDMETHODIMP CGame::TextDisplaySprite(long lSpriteNum, BSTR bstrString, int lX, int lY, BOOL bTransparent, BOOL bErase)
{

	HDC hdcSprite;
	CImageHigh *imgFrame;

	// Get a hdc to sprite's current frame
	imgFrame = m_sprDynamic[lSpriteNum]->GetFrameSource(m_sprDynamic[lSpriteNum]->GetFrame());
	hdcSprite = imgFrame->GetSourceDC();

	// should we erase with bgrnd color first?
	if (bErase) {
		HRGN hRgn;          // Handle to a region object  
		HBRUSH hBrush;      // Handle to a brush object 
		RECT rect = { 0, 0, m_sprDynamic[lSpriteNum]->ImageWidth(), m_sprDynamic[lSpriteNum]->ImageHeight() };

		// Create a rectangular region.
		hRgn = CreateRectRgn (0, 0, rect.right, rect.bottom);

		// Create a solid brush.
		hBrush = CreateSolidBrush (RGB(m_DrawBackRed,m_DrawBackGreen,m_DrawBackBlue)); 

		// Fill the region out with the created brush.
		FillRgn (hdcSprite, hRgn, hBrush);

		// Delete the rectangular region. 
		DeleteObject (hRgn);

		// Delete the brush object and free all resources associated with it.
		DeleteObject (hBrush);
	}	



	// Display text using selected color, font attribs
	BYTE iMode;
	if (bTransparent)
		iMode = TRANSPARENT;
	else
		iMode = OPAQUE;
	
	HFONT holdFont, hFont;
	hFont = CreateFontIndirect(&m_lfStyle);
	if (hFont == NULL)
		Error(_T("TextDisplay failed on call to CreateFontIndirect"));

	holdFont = (HFONT) SelectObject(hdcSprite, hFont);
	SetTextColor(hdcSprite, RGB(m_DrawRed,m_DrawGreen,m_DrawBlue));
    SetBkMode(hdcSprite, iMode);
	ExtTextOut(hdcSprite, lX, lY, 0, NULL, bstrString, wcslen(bstrString), NULL);
	SelectObject(hdcSprite, holdFont);
	DeleteObject(hFont);

	return S_OK;
}

STDMETHODIMP CGame::DrawSetPixelSprite(long lSpriteNum, int x, int y, BOOL bErase)
{
	COLORREF  clrSet = RGB(m_DrawRed,m_DrawGreen,m_DrawBlue);
	CImageHigh * imgFrame;
	HDC hdcSprite;

	// Get a hdc to sprite's current frame
	imgFrame = m_sprDynamic[lSpriteNum]->GetFrameSource(m_sprDynamic[lSpriteNum]->GetFrame());
	hdcSprite = imgFrame->GetSourceDC();

	// should we erase with bgrnd color first?
	if (bErase) {
		HRGN hRgn;          // Handle to a region object  
		HBRUSH hBrush;      // Handle to a brush object 
		RECT rect = { 0, 0, m_sprDynamic[lSpriteNum]->ImageWidth(), m_sprDynamic[lSpriteNum]->ImageHeight() };

		// Create a rectangular region.
		hRgn = CreateRectRgn (0, 0, rect.right, rect.bottom);

		// Create a solid brush.
		hBrush = CreateSolidBrush (RGB(m_DrawBackRed,m_DrawBackGreen,m_DrawBackBlue)); 

		// Fill the region out with the created brush.
		FillRgn (hdcSprite, hRgn, hBrush);

		// Delete the rectangular region. 
		DeleteObject (hRgn);

		// Delete the brush object and free all resources associated with it.
		DeleteObject (hBrush);
	}	


	// Draw to back buffer
	SetPixel (hdcSprite, x, y, clrSet);

	return S_OK;
}


STDMETHODIMP CGame::DrawFullScreen(BOOL bShowTaskBar, BOOL bShowSIP, BOOL bShowStartIcon)
{
#ifndef HPC_BUILD
	// TODO: Add your implementation code here
	// SHFS_SHOWTASKBAR            0x0001
	// SHFS_HIDETASKBAR            0x0002
	// SHFS_SHOWSIPBUTTON          0x0004
	// SHFS_HIDESIPBUTTON          0x0008
	// SHFS_SHOWSTARTICON          0x0010
	// SHFS_HIDESTARTICON          0x0020
	long lRet;

	if (bShowTaskBar && bShowSIP && bShowStartIcon)
		return S_OK;

	HWND hwndMenu;
	hwndMenu = FindWindow(_T("menu_worker"), _T(""));
	if (hwndMenu != 0) {	//window found 
		::ShowWindow(hwndMenu, SW_HIDE);  //SHOWNORMAL
	}
	lRet = SetForegroundWindow(m_hwnd);
	if (!(bShowTaskBar || bShowSIP || bShowStartIcon)) { // Full Screen
		lRet = ::MoveWindow(m_hwnd, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) + (HHTASKBARHEIGHT), 0);
	} else {
	    lRet = ::MoveWindow(m_hwnd, 0, HHTASKBARHEIGHT, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), TRUE);
	}
	
	if (bShowStartIcon) 
		lRet = SHFullScreen(m_hwnd, SHFS_SHOWSTARTICON);
	else
		lRet = SHFullScreen(m_hwnd, SHFS_HIDESTARTICON);
		
	if (bShowTaskBar) 
		lRet = SHFullScreen(m_hwnd, SHFS_SHOWTASKBAR);
	else
		lRet = SHFullScreen(m_hwnd, SHFS_HIDETASKBAR);

	if (bShowSIP) 
		lRet = SHFullScreen(m_hwnd, SHFS_SHOWSIPBUTTON);
	else
		lRet = SHFullScreen(m_hwnd, SHFS_HIDESIPBUTTON);

	m_bAutoFullScreen = TRUE;
	m_bShowTaskBar = bShowTaskBar;
	m_bShowSIP = bShowSIP;
	m_bShowStartIcon = bShowStartIcon;

#endif
	return S_OK;
}

STDMETHODIMP CGame::get_DrawBackgroundShouldScroll(long lSpriteNum, int iDirection, BOOL *pVal)
{
	// returns TRUE if lSpriteNum (the controlling sprite) should cause
	// background to scroll
    int iSpriteX, iSpriteY;
	int iSpriteCenterX, iSpriteCenterY;

    iSpriteCenterX = m_iBoundsWidth / 2 - (m_sprDynamic[lSpriteNum]->ImageWidth()/2);
    iSpriteCenterY = m_iBoundsHeight / 2 - (m_sprDynamic[lSpriteNum]->ImageHeight()/2);


    iDirection = (iDirection / 45) % 8;
	iSpriteX = m_sprDynamic[lSpriteNum]->GetX();
	iSpriteY = m_sprDynamic[lSpriteNum]->GetY();

	*pVal = FALSE;

    if ((iDirection == 7 || iDirection <= 1) && (iSpriteX >= iSpriteCenterX)) 
        *pVal = TRUE;
    else
        if ((iDirection >= 1 && iDirection <= 3) && (iSpriteY >= iSpriteCenterY)) 
			*pVal = TRUE;
        else
            if ((iDirection >= 3 && iDirection <= 5) && (iSpriteX <= iSpriteCenterX)) 
		        *pVal = TRUE;
            else
                if ((iDirection >= 5 && iDirection <= 7) && (iSpriteY <= iSpriteCenterY)) 
			        *pVal = TRUE;

	return S_OK;
}



STDMETHODIMP CGame::TileSetCollisionTiles(VARIANT *arrCollisionTiles)
{
	// *********
	// ** next get arrCollisionTiles - the array of collision tiles
    // *********
	long lLBound1,lLBound2,lUBound1,lUBound2;
	int xx;
	SAFEARRAY ** psaCollision;
	if (arrCollisionTiles->vt==(VT_ARRAY| VT_VARIANT) ||
		arrCollisionTiles->vt==(VT_BYREF | VT_ARRAY| VT_VARIANT) ) { 

		// create a safearray
		if (arrCollisionTiles->vt==(VT_ARRAY| VT_VARIANT))
			psaCollision = &arrCollisionTiles->parray;
		else
			psaCollision = arrCollisionTiles->pparray;

	
		long lDims = SafeArrayGetDim(*psaCollision);
		if (lDims!=1) 
			return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrCollisionTiles must be a 1-dimensional array", IID_IGame, 0);

		SafeArrayGetLBound(*psaCollision,1,&lLBound1);    
		SafeArrayGetUBound(*psaCollision,1,&lUBound1);    
		
		if (lUBound1>MAX_TILES_COLLISION)
			return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrCollisionTiles contains too many collision tiles.", IID_IGame, 0);

		m_iNumTilesCollision = lUBound1;


		HRESULT hr;
		void * pData;
		hr = SafeArrayAccessData( *psaCollision, &pData );
		if( FAILED(hr) )
			return hr;
		VARIANT* vData = static_cast<VARIANT*>(pData);

		// now access array elements as pDoubleData[ y * height + x ]
		int iOffset=0;
		for (int xx=lLBound1;xx<=lUBound1;xx++) {
				m_iTilesCollision[xx] = vData[iOffset].iVal;
				iOffset++;
			}

		// release array contents
		SafeArrayUnaccessData(*psaCollision);


		
	} else
		return AtlReportError(GetObjectCLSID(), "DrawSetTileMap: parameter arrCollisionTiles must be a 1-dimensional array of frame values.", IID_IGame, 0);


	return S_OK;
}

STDMETHODIMP CGame::SpriteCopyFrame(long lSrcSpriteNum, int lSrcFrame, long lDestSpriteNum, int lDestFrame)
{
	// TODO: Add your implementation code here
	BitBlt(m_sprDynamic[lDestSpriteNum]->m_sprites[lDestFrame]->GetSourceDC(),
		0, 0, m_sprDynamic[lSrcSpriteNum]->ImageWidth(), m_sprDynamic[lSrcSpriteNum]->ImageHeight(), 
		m_sprDynamic[lSrcSpriteNum]->m_sprites[lSrcFrame]->GetSourceDC(),
		0, 0, SRCCOPY);

	return S_OK;
}

STDMETHODIMP CGame::SpriteFrameMirrorInternal(long lSpriteNum, BOOL bMirror)
{
	// copy amd scale sprite to default DC
	BOOL bRetVal;
    HBITMAP hBmpTemp;
	HBITMAP hBmpOld;
    HDC hdcBmpTemp;
	HDC hdcSprite;

	// Get a hdc to sprite's current frame
	CImageHigh *imgFrame;
	imgFrame = m_sprDynamic[lSpriteNum]->GetFrameSource(m_sprDynamic[lSpriteNum]->GetFrame());
	hdcSprite = imgFrame->GetSourceDC();
	int iWidth = imgFrame->ImageWidth();
	int iHeight = imgFrame->ImageHeight();

	// make a temporary holding area
    hBmpTemp = CreateCompatibleBitmap(hdcSprite, iWidth, iHeight);
    if (hBmpTemp == NULL) {
        DWORD lError = GetLastError();
		return FALSE;
	}
    hdcBmpTemp = CreateCompatibleDC(imgFrame->GetScreenDC());
    if (hdcBmpTemp == NULL)
        return FALSE;
    hBmpOld = SelectBitmap(hdcBmpTemp, hBmpTemp);
    
	//if( bLateral )
	//	pDC->StretchBlt( xDest, yDest, bm.bmWidth, bm.bmHeight, &memDC, 
	//			bm.bmWidth-1, 0, -bm.bmWidth, bm.bmHeight, SRCCOPY );
	//else
	//	pDC->StretchBlt( xDest, yDest, bm.bmWidth, bm.bmHeight, &memDC, 
	//			0, bm.bmHeight-1, bm.bmWidth, -bm.bmHeight, SRCCOPY );


	if (bMirror) {
		bRetVal = StretchBlt(
			hdcBmpTemp,  //HDC hdcDest,
			0, 
			0, 
			iWidth, 
			iHeight,
			hdcSprite,   //HDC hdcSrc,
			iWidth, 
			0, 
			-iWidth, 
			iHeight,
			SRCCOPY);
	} else {
		bRetVal = StretchBlt(
			hdcBmpTemp,  //HDC hdcDest,
			0, 
			0, 
			iWidth, 
			iHeight,
			hdcSprite,   //HDC hdcSrc,
			0, 
			iHeight, 
			iWidth, 
			-iHeight,
			SRCCOPY);
	}

	bRetVal = BitBlt(hdcSprite,
		0,0,
		iWidth,
		iHeight,
		hdcBmpTemp,
		0,0,
		SRCCOPY);

	// cleanup
	SelectObject(hdcBmpTemp, hBmpOld);
	DeleteObject(hBmpTemp);
	DeleteDC(hdcBmpTemp);

	return S_OK;
}

STDMETHODIMP CGame::SpriteFrameFlip(long lSpriteNum)
{

	SpriteFrameMirrorInternal(lSpriteNum, FALSE);

	return S_OK;
}

STDMETHODIMP CGame::SpriteFrameMirror(long lSpriteNum)
{

	SpriteFrameMirrorInternal(lSpriteNum, TRUE);
	return S_OK;
}

STDMETHODIMP CGame::get_SpriteDirectionRate(long lSpriteNum, short *pVal)
{
	// Get the # of frames that pass between sprite position updates
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteDirectionRate: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	
	*pVal = m_sprDynamic[lSpriteNum]->GetVelocityIncrement();

	return S_OK;
}

STDMETHODIMP CGame::put_SpriteDirectionRate(long lSpriteNum, short newVal)
{
	// Set the # of frames that pass between sprite position updates
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteDirectionRate: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	m_sprDynamic[lSpriteNum]->SetVelocityIncrement(newVal);

	return S_OK;
}

STDMETHODIMP CGame::get_DrawHwndControl(DWORD *pVal)
{
	// return the Hwnd for the control window
	*pVal = (DWORD) m_hWnd;

	return S_OK;
}

STDMETHODIMP CGame::get_SpriteCollisionAccuracy(long lSpriteNum, short *pVal)
{
	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteCollisionAccuracy: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);

	*pVal = m_sprDynamic[lSpriteNum]->GetSpriteCollisionAccuracy();
	return S_OK;
}

STDMETHODIMP CGame::put_SpriteCollisionAccuracy(long lSpriteNum, short newVal)
{
	// if Sprite's Collision Accuracy is >0 then collision checks are
	// done each N pixels instead of on move only.

	if (m_sprDynamic[lSpriteNum]==NULL)
		return AtlReportError(GetObjectCLSID(), "SpriteCollisionAccuracy: You have not defined that sprite yet. Check lSpriteNum parameter.", IID_IGame, 0);
	
	m_sprDynamic[lSpriteNum]->SetSpriteCollisionAccuracy(newVal);
	return S_OK;
}


STDMETHODIMP CGame::SpriteSetCollisionSprites(long lSpriteNum, VARIANT *arrCollisionSprites)
{
	// *********
	// ** set the sprites that 
    // *********
	long lLBound1,lLBound2,lUBound1,lUBound2;
	int xx;
	SAFEARRAY ** psaCollision;
	if (arrCollisionSprites->vt==(VT_ARRAY| VT_VARIANT) ||
		arrCollisionSprites->vt==(VT_BYREF | VT_ARRAY| VT_VARIANT) ) { 

		// create a safearray
		if (arrCollisionSprites->vt==(VT_ARRAY| VT_VARIANT))
			psaCollision = &arrCollisionSprites->parray;
		else
			psaCollision = arrCollisionSprites->pparray;

	
		long lDims = SafeArrayGetDim(*psaCollision);
		if (lDims!=1) 
			return AtlReportError(GetObjectCLSID(), "SpriteSetCollisionSprites: parameter arrCollisionSprites must be a 1-dimensional array", IID_IGame, 0);

		SafeArrayGetLBound(*psaCollision,1,&lLBound1);    
		SafeArrayGetUBound(*psaCollision,1,&lUBound1);    
		
		if (lUBound1>MAX_DYNAMIC_SPRITES)
			return AtlReportError(GetObjectCLSID(), "SpriteSetCollisionSprites: parameter arrCollisionSprites contains too many collision sprites.", IID_IGame, 0);

		m_iNumTilesCollision = lUBound1;


		HRESULT hr;
		void * pData;
		hr = SafeArrayAccessData( *psaCollision, &pData );
		if( FAILED(hr) )
			return hr;
		VARIANT* vData = static_cast<VARIANT*>(pData);

		// now access array elements as pDoubleData[ y * height + x ]
		int iOffset=0;
		short iSpritesCollision[MAX_DYNAMIC_SPRITES];
		short *iptSprColl = iSpritesCollision;
		for (int xx=lLBound1;xx<=lUBound1;xx++) {
			iSpritesCollision[xx] = vData[iOffset].iVal;
			iOffset++;
		}

		// release array contents
		SafeArrayUnaccessData(*psaCollision);

		m_sprDynamic[lSpriteNum]->SetCollisionArray(iptSprColl,iOffset);
			
	} else
		return AtlReportError(GetObjectCLSID(), "SpriteSetCollisionSprites: parameter arrCollisionSprites must be a 1-dimensional array of SpriteNums.", IID_IGame, 0);


	return S_OK;
}


STDMETHODIMP CGame::get_TileExtendedCollisions(BOOL *pVal)
{
	*pVal = m_bTileExtendedCollisions;

	return S_OK;
}

STDMETHODIMP CGame::put_TileExtendedCollisions(BOOL newVal)
{
	m_bTileExtendedCollisions = newVal;

	return S_OK;
}
